package umage.figures;

import static org.jhotdraw.draw.AttributeKeys.FONT_BOLD;
import static org.jhotdraw.draw.AttributeKeys.FONT_ITALIC;

import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.geom.Point2D.Double;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;

import org.jhotdraw.draw.AttributeKeys;
import org.jhotdraw.draw.CreationTool;
import org.jhotdraw.draw.Figure;
import org.jhotdraw.draw.FigureEvent;
import org.jhotdraw.draw.FigureListener;
import org.jhotdraw.draw.GraphicalCompositeFigure;
import org.jhotdraw.draw.ListFigure;
import org.jhotdraw.draw.RectangleFigure;
import org.jhotdraw.draw.TextFigure;
import org.jhotdraw.draw.VerticalLayouter;
import org.jhotdraw.xml.DOMInput;
import org.jhotdraw.xml.DOMOutput;

@FigureInfo(name = "Class Figure", icon = "ClassFigure.png", toolbar = "Figures", toolbarPriority = 0, tool = CreationTool.class)
public class ClassFigure extends GraphicalCompositeFigure {

	protected TextFigure nameFigure;
	protected ListFigure fieldList;
	protected ListFigure methodList;
	private int _diameter;
	private int _centerX;
	private int _centerY;
	private Rectangle _class;

	public ClassFigure() {
		super(new RectangleFigure());
		setLayouter(new VerticalLayouter());

		fieldList = new ListFigure();
		methodList = new ListFigure();

		nameFigure = new TextFigure("Class Name");
		nameFigure.addFigureListener(new NameChangeListener(nameFigure));

		updateList();

	}

	protected void updateList() {
		removeAllChildren();
		add(nameFigure);
		FONT_BOLD.set(nameFigure, true);
		add(new SeparatorLineFigure());
		add(fieldList);
		add(new SeparatorLineFigure());
		add(methodList);
	}

	@Override
	public Collection<Action> getActions(Double p) {
		Collection<Action> actions = new ArrayList<Action>();

		// Determine if a valid child is included in the selection
		Figure targetFigure = findFigureInside(p);

		if (targetFigure != null && targetFigure instanceof TextFigure && !fieldList.contains(p)) {
			actions.add(new ModifyAbstractAction(targetFigure));
		}
		actions.add(new AddTextFigureAction(fieldList, "Add Field", "+ attr : int"));
		actions.add(new AddTextFigureAction(methodList, "Add Method", "+ operation() : void"));
		return actions;
	}

	protected class DeleteTextFigureAction extends AbstractAction {
		protected ListFigure list;
		protected TextFigure figure;

		public DeleteTextFigureAction(ListFigure list, TextFigure figure, String title) {
			super(title);
			this.list = list;
			this.figure = figure;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			list.remove(figure);
		}

	}

	protected class AddTextFigureAction extends AbstractAction {
		protected ListFigure list;
		protected String initText;

		public AddTextFigureAction(ListFigure list, String title, String initText) {
			super(title);
			this.list = list;
			this.initText = initText;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			TextFigure textFigure = new TextFigure();
			textFigure.setText(initText);
			textFigure.setAttributeEnabled(FONT_BOLD, false);
			textFigure.setInteractive(true);
			textFigure.addFigureListener(new TextFigureChangeListener(list, textFigure));
			list.add(textFigure);
		}

	}

	protected class TextFigureChangeListener implements FigureListener {
		protected ListFigure list;
		protected Figure figure;

		public TextFigureChangeListener(ListFigure list, Figure figure) {
			this.list = list;
			this.figure = figure;
		}

		@Override
		public void figureAttributeChanged(FigureEvent e) {
			if (e.getAttribute() == AttributeKeys.TEXT) {
				String newValue = (String) e.getNewValue();
				if (newValue == null || newValue.isEmpty())
					list.remove(figure);
			}
		}

		@Override
		public void figureAreaInvalidated(FigureEvent e) {
		}

		@Override
		public void figureChanged(FigureEvent e) {
		}

		@Override
		public void figureAdded(FigureEvent e) {
		}

		@Override
		public void figureRemoved(FigureEvent e) {
		}

		@Override
		public void figureRequestRemove(FigureEvent e) {
		}

	}

	protected class NameChangeListener implements FigureListener {
		protected TextFigure figure;

		public NameChangeListener(TextFigure figure) {
			this.figure = figure;
		}

		@Override
		public void figureAttributeChanged(FigureEvent e) {
			if (e.getAttribute() == AttributeKeys.TEXT) {
				String newValue = (String) e.getNewValue();
				if (newValue == null || newValue.isEmpty()) {
					figure.setText((String) e.getOldValue());
				}
			}
		}

		@Override
		public void figureAreaInvalidated(FigureEvent e) {
		}

		@Override
		public void figureChanged(FigureEvent e) {
		}

		@Override
		public void figureAdded(FigureEvent e) {
		}

		@Override
		public void figureRemoved(FigureEvent e) {
		}

		@Override
		public void figureRequestRemove(FigureEvent e) {
		}
	}

	protected class ModifyAbstractAction extends AbstractAction {
		protected Figure target;

		public ModifyAbstractAction(Figure target) {
			super(getAbstract(target) ? "-abstract" : "+abstract");
			this.target = target;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			setAbstract(target, !getAbstract(target));
		}

	}

	public String getName() {
		return nameFigure.getText();
	}

	public void setName(String name) {
		nameFigure.setText(name);
	}

	public boolean isAbstract() {
		return getAbstract(nameFigure);
	}

	public void setAbstract(boolean a) {
		setAbstract(nameFigure, a);
	}

	public boolean isAbstract(String method) {
		Figure target = getMethod(method);
		if (target == null)
			return false;
		return getAbstract(target);
	}

	public void setAbstract(String method, boolean a) {
		Figure target = getMethod(method);
		if (target != null)
			setAbstract(target, a);
	}

	protected boolean getAbstract(Figure target) {
		return FONT_ITALIC.get(target);
	}

	protected void setAbstract(Figure target, boolean a) {
		FONT_ITALIC.set(target, a);
	}

	protected TextFigure getMethod(String method) {
		for (int i = 0; i < methodList.getChildCount(); i++) {
			Figure methodFigure = methodList.getChild(i);
			if (methodFigure instanceof TextFigure && ((TextFigure) methodFigure).getText().equals(method)) {
				return (TextFigure) methodFigure;
			}
		}
		return null;
	}

	public void addMethod(String method) {
		removeMethod(method);
		TextFigure textFigure = new TextFigure();
		textFigure.setText(method);
		textFigure.setAttributeEnabled(FONT_BOLD, false);
		textFigure.setInteractive(true);
		textFigure.addFigureListener(new TextFigureChangeListener(methodList, textFigure));
		methodList.add(textFigure);
	}

	public void removeMethod(String method) {
		TextFigure figure = getMethod(method);
		if (figure != null)
			methodList.remove(figure);
	}

	public List<String> getMethods() {
		LinkedList<String> methods = new LinkedList<String>();
		for (Figure methodFigure : methodList.getChildren()) {
			if (methodFigure instanceof TextFigure) {
				methods.add(((TextFigure) methodFigure).getText());
			}
		}
		return methods;
	}

	protected TextFigure getField(String field) {
		for (int i = 0; i < fieldList.getChildCount(); i++) {
			Figure fieldFigure = fieldList.getChild(i);
			if (fieldFigure instanceof TextFigure && ((TextFigure) fieldFigure).getText().equals(field)) {
				return (TextFigure) fieldFigure;
			}
		}
		return null;
	}

	public void addField(String field) {
		removeField(field);
		TextFigure textFigure = new TextFigure();
		textFigure.setText(field);
		textFigure.setAttributeEnabled(FONT_BOLD, false);
		textFigure.setInteractive(true);
		textFigure.addFigureListener(new TextFigureChangeListener(fieldList, textFigure));
		fieldList.add(textFigure);
	}

	public void removeField(String field) {
		TextFigure figure = getField(field);
		if (figure != null)
			fieldList.remove(figure);
	}

	public List<String> getFields() {
		LinkedList<String> fields = new LinkedList<String>();
		for (Figure fieldFigure : fieldList.getChildren()) {
			if (fieldFigure instanceof TextFigure) {
				fields.add(((TextFigure) fieldFigure).getText());
			}
		}
		return fields;
	}

	protected void reattachListeners() {
		nameFigure.addFigureListener(new NameChangeListener(nameFigure));
		for (Figure child : fieldList.getChildren()) {
			if (child instanceof TextFigure) {
				child.addFigureListener(new TextFigureChangeListener(fieldList, child));
			}
		}
		for (Figure child : methodList.getChildren()) {
			if (child instanceof TextFigure) {
				child.addFigureListener(new TextFigureChangeListener(methodList, child));
			}
		}
	}

	public ClassFigure clone() {
		ClassFigure that = (ClassFigure) super.clone();
		that.nameFigure = nameFigure.clone();
		that.fieldList = (ListFigure) fieldList.clone();
		that.methodList = (ListFigure) methodList.clone();
		that.reattachListeners();
		that.updateList();
		return that;
	}

	public void write(DOMOutput out) throws IOException {
		out.writeObject(getPresentationFigure());
		out.writeObject(nameFigure);
		out.writeObject(fieldList);
		out.writeObject(methodList);
	}

	@Override
	public void read(DOMInput in) throws IOException {
		setPresentationFigure((Figure) in.readObject(0));
		nameFigure = (TextFigure) in.readObject(1);
		fieldList = (ListFigure) in.readObject(2);
		methodList = (ListFigure) in.readObject(3);
		reattachListeners();
		updateList();
	}

	public void setCenter(int x, int y) {
		int minValue = _diameter / 2;
		if (x < minValue) {
			_centerX = minValue;
		} else {
			_centerX = x;
		}
		if (y < minValue) {
			_centerY = minValue;
		} else {
			_centerY = y;
		}
		_centerX = x;
		_centerY = y;
		_class.setLocation(_centerX - minValue, _centerY - minValue);
	}
}